//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using OpenLS.Drawing;

namespace OpenLS.Spreadsheet.UI
{
    class ColorToBrushConverter : IMultiValueConverter
    {
        [ThreadStatic] private static Dictionary<Pair<SpreadsheetColor, ColorScheme>, SolidColorBrush> _brushTable;
            

        private static Dictionary<Pair<SpreadsheetColor, ColorScheme>, SolidColorBrush> brushTable
        {
            get
            {
                if (_brushTable == null)
                    _brushTable = new Dictionary<Pair<SpreadsheetColor, ColorScheme>, SolidColorBrush>();
                return _brushTable;
            }
        }

        #region IMultiValueConverter Members

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var colorScheme = StrictConverter.Convert<ColorScheme>( values[1] );
            if (values[0] == null)
                return null;
            if (values[0] == DependencyProperty.UnsetValue)
                return null;
            var r = (SpreadsheetColor)values[0];
            if (r != null)
                return GetBrush(r, colorScheme);
            var f = values[0] as PatternFill;
            if (f != null)
                return GetBrush(f.GetColor(), colorScheme);
            throw new NotSupportedException();
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }

        #endregion

        internal static SolidColorBrush GetBrush(SpreadsheetColor colorReference, ColorScheme colorScheme)
        {
            if (colorReference == null)
                return null;
            SolidColorBrush result;
            var key = new Pair<SpreadsheetColor, ColorScheme>(colorReference, colorScheme);
            if (!brushTable.TryGetValue(key, out result))
            {
                result = new SolidColorBrush(colorReference.GetColor(colorScheme));
                result.Freeze();
                brushTable.Add(key, result);
            }
            return result;
        }

        internal static Brush GetBrushFromGradient(GradientFill gradientFill, ColorScheme colorScheme)
        {
            switch (gradientFill.GradientType)
            {
                case GradientType.Linear:
                    {
                        var result = new LinearGradientBrush();
                        foreach (var stop in gradientFill.Stops)
                        {
                            var ss =
                                new System.Windows.Media.GradientStop(stop.Color.GetColor(colorScheme), stop.Position);
                            result.GradientStops.Add(ss);
                        }
                        double angle = gradientFill.Degree/180*Math.PI;
                        result.EndPoint = new Point(0.5 + 0.5*Math.Cos(angle), 0.5 + 0.5*Math.Sin(angle));
                        result.StartPoint = new Point(0.5 - 0.5*Math.Cos(angle), 0.5 - 0.5*Math.Sin(angle));
                        result.Freeze();
                        Contract.Assert(result.GradientStops.Count > 0);
                        return result;
                    }
                case GradientType.Path:
                    {
                        var result = new DrawingBrush {Viewport = new Rect(0, 0, 1, 1)};
                        // result.ViewportUnits = BrushMappingMode.Absolute;
                        var gr = new DrawingGroup();

                        result.Drawing = gr;
                        gr.Children.Add(new GeometryDrawing(Brushes.Transparent, null,
                                                            new RectangleGeometry(new Rect(0, 0, 1, 1))));
                        
                        gr.Children.Add(getPathDrawing(gradientFill.Stops, colorScheme, new Point(1, 0), new Point(0, 0), //left
                                                       new Point(0, 0), new Point(gradientFill.Left, gradientFill.Top), new Point(gradientFill.Left, gradientFill.Bottom), new Point(0, 1)));
                        gr.Children.Add(getPathDrawing(gradientFill.Stops, colorScheme, new Point(0, 1), new Point(0, 0), // top
                                                       new Point(0, 0), new Point(gradientFill.Left, gradientFill.Top), new Point(gradientFill.Right, gradientFill.Top), new Point(1, 0)));
                        gr.Children.Add(getPathDrawing(gradientFill.Stops, colorScheme, new Point(0, 0), new Point(1, 0), // right
                                                       new Point(1, 0), new Point(gradientFill.Right, gradientFill.Top), new Point(gradientFill.Right, gradientFill.Bottom), new Point(1, 1)));
                        gr.Children.Add(getPathDrawing(gradientFill.Stops, colorScheme, new Point(0, 0), new Point(0, 1), // bottom
                                                       new Point(1, 1), new Point(gradientFill.Right, gradientFill.Bottom), new Point(gradientFill.Left, gradientFill.Bottom), new Point(0, 1)));
                        if (gradientFill.Right > gradientFill.Left && gradientFill.Bottom > gradientFill.Top)
                            gr.Children.Add(getPathDrawing(new List<GradientStop>(){gradientFill.Stops[0]}, colorScheme, new Point(0, 0), new Point(0, 1), // middle

                                                           new Point(gradientFill.Left, gradientFill.Top),
                                                           new Point(gradientFill.Right, gradientFill.Top),
                                                           new Point(gradientFill.Right, gradientFill.Bottom),
                                                           new Point(gradientFill.Left, gradientFill.Bottom),
                                                           new Point(gradientFill.Left, gradientFill.Bottom)
                                                           ));
                        result.Freeze();

                        return result;
                    }
                    default:
                    throw new NotSupportedException();
            }
        }
        static GeometryDrawing getPathDrawing(IEnumerable<GradientStop>gradientStops, ColorScheme colorScheme, Point startPoint, Point endPoint, Point sp, params Point[] points)
        {
            var segments = new List<PathSegment>();
            foreach (Point p in points)
                segments.Add(new LineSegment(p, false));
            var pathFigure = new PathFigure(sp, segments, false);
            var pathGeometry = new PathGeometry(new[] { pathFigure });

            return new GeometryDrawing(
                new LinearGradientBrush(getGradientStops(colorScheme, gradientStops)) { StartPoint = startPoint, EndPoint = endPoint },
                null, pathGeometry);
        }
        static GradientStopCollection getGradientStops(ColorScheme colorScheme, IEnumerable<GradientStop> GradientStops)
        {
            var result = new GradientStopCollection();
            foreach (GradientStop stop in GradientStops)
                result.Add(new System.Windows.Media.GradientStop(stop.Color.GetColor(colorScheme), stop.Position));
            return result;
        }

    }
}